//
// Created by deng on 2022/12/25.
//
#include <malloc.h>
#include "bsp_stmflash.h"

/**
 * @brief       解锁STM32的FLASH
 * @param       无
 * @retval      无
 */
void stmflash_unlock(void){
    FLASH->KEYR = STM32_FLASH_KEY1;
    FLASH->KEYR = STM32_FLASH_KEY2;
}

/**
 * @brief       flash上锁
 * @param       无
 * @retval      无
 */
void stmflash_lock(void){
    FLASH->CR |= (uint32_t)(1<<31);
}

/**
 * @brief       得到FLASH的错误状态
 * @param       无
 * @retval      执行结果
 *   @arg       0   : 已完成
 *   @arg       其他 : 错误编号
 */

uint8_t  stmflash_get_error_status(void){
    uint32_t  res = 0;
    res = FLASH->SR;

    if(res & (1<<16))   return 1;/* BSY=1, 繁忙 */
    if(res & (1<<7))    return 2;/* PGSERR=1,编程序列错误 */
    if(res & (1<<6))    return 3;/* PGPERR=1,编程并行位数错误 */
    if(res & (1<<5))    return 4;/* PGAERR=1,编程对齐错误 */
    if(res & (1<<4))    return 5;/* WRPERR=1,写保护错误 */
    return 0;/* 没有任何状态/操作完成. */
}

/**
 * @brief       等待操作完成
 * @param       time : 要延时的长短
 * @retval      执行结果
 *   @arg       0   : 已完成
 *   @arg       0XFF: 超时
 *   @arg       其他 : 错误编号
 */
uint8_t stmflash_wait_done(uint32_t time)
{
    uint8_t res;

    do
    {
        res = stmflash_get_error_status();

        if (res != 1)
        {
            break;      /* 非忙, 无需等待了, 直接退出 */
        }

        time--;
    } while (time);

    if (time == 0)res = 0XFF;   /* 超时 */

    return res;
}

/**
* @brief       擦除扇区
* @param       saddr   : 扇区地址 0 ~ 11
*                        0~3, 16K扇区; 4,64K扇区; 5~11, 128K扇区.
* @retval      执行结果
*   @arg       0   : 已完成
*   @arg       0XFF: 超时
*   @arg       其他 : 错误编号
*/
uint8_t stmflash_erase_sector(uint32_t saddr)
{
    uint8_t res = 0;

    res = stmflash_wait_done(0XFFFFFFFF);   /* 等待上次操作结束 */

    if (res == 0)
    {
        FLASH->CR &= ~(3 << 8);             /* 清除PSIZE原来的设置 */
        FLASH->CR |= 2 << 8;                /* 设置为32bit宽,确保VCC=2.7~3.6V之间!! */
        FLASH->CR &= ~(0X1F << 3);          /* 清除原来的设置 */
        FLASH->CR |= saddr << 3;            /* 设置要擦除的扇区 */
        FLASH->CR |= 1 << 1;                /* 扇区擦除 */
        FLASH->CR |= 1 << 16;               /* 开始擦除 */
        res = stmflash_wait_done(0XFFFFFFFF);   /* 等待操作结束 */

        if (res != 1)                       /* 非忙 */
        {
            FLASH->CR &= ~(1 << 1);         /* 清除扇区擦除标志 */
        }
    }

    return res;
}

/**
 * @brief       在FLASH指定地址写一个字 (32位数据)
 *   @note      这了写入一个字, 是指4个字节
 * @param       faddr   : 写入地址 (此地址必须为4的倍数!!)
 * @param       data    : 要写入的数据(32位)
 * @retval      执行结果
 *   @arg       0   : 已完成
 *   @arg       0XFF: 超时
 *   @arg       其他 : 错误编号
 */
uint8_t stmflash_write_word(uint32_t faddr, uint32_t data)
{
    uint8_t res;

    res = stmflash_wait_done(0XFFFFF);

    if (res == 0)   /* OK */
    {
        FLASH->CR &= ~(3 << 8);             /* 清除PSIZE原来的设置 */
        FLASH->CR |= 2 << 8;                /* 设置为32bit宽,确保VCC=2.7~3.6V之间!! */
        FLASH->CR |= 1 << 0;                /* 编程使能 */
        *(volatile uint32_t *)faddr = data; /* 写入数据 */
        res = stmflash_wait_done(0XFFFFF);  /* 等待操作完成,一个字编程 */

        if (res != 1)                       /* 操作成功 */
        {
            FLASH->CR &= ~(1 << 0);         /* 清除PG位 */
        }
    }

    return res;
}

/**
 * @brief       从指定地址读取一个字 (32位数据)
 * @param       faddr   : 读取地址 (此地址必须为4的倍数!!)
 * @retval      读取到的数据 (32位)
 */
uint32_t stmflash_read_word(uint32_t faddr)
{
    return *(volatile uint32_t *)faddr;
}

/**
 * @brief       获取某个地址所在的flash扇区
 * @param       faddr   : flash地址
 * @retval      0~11, 即addr所在的扇区
 */
uint8_t stmflash_get_flash_sector(uint32_t addr)
{
    if (addr < ADDR_FLASH_SECTOR_1)return 0;
    else if (addr < ADDR_FLASH_SECTOR_2)return 1;
    else if (addr < ADDR_FLASH_SECTOR_3)return 2;
    else if (addr < ADDR_FLASH_SECTOR_4)return 3;
    else if (addr < ADDR_FLASH_SECTOR_5)return 4;
    else if (addr < ADDR_FLASH_SECTOR_6)return 5;
    else if (addr < ADDR_FLASH_SECTOR_7)return 6;
    else if (addr < ADDR_FLASH_SECTOR_8)return 7;
    else if (addr < ADDR_FLASH_SECTOR_9)return 8;
    else if (addr < ADDR_FLASH_SECTOR_10)return 9;
    else if (addr < ADDR_FLASH_SECTOR_11)return 10;

    return 11;
}

/**
 * @brief       在FLASH 指定位置, 写入指定长度的数据(自动擦除)
 *   @note      因为STM32F4的扇区实在太大,没办法本地保存扇区数据,所以本函数写地址如果非0XFF
 *              ,那么会先擦除整个扇区且不保存扇区数据.所以写非0XFF的地址,将导致整个扇区数据丢失.
 *              建议写之前确保扇区里没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写.
 *              该函数对OTP区域也有效!可以用来写OTP区!
 *              OTP区域地址范围:0X1FFF7800~0X1FFF7A0F(注意：最后16字节，用于OTP数据块锁定，别乱写！！)
 * @param       waddr   : 起始地址 (此地址必须为4的倍数!!,否则写入出错!)
 * @param       pbuf    : 数据指针
 * @param       length  : 要写入的 字(32位)数(就是要写入的32位数据的个数)
 * @retval      无
 */
void stmflash_write(uint32_t waddr, uint32_t *pbuf, uint32_t length)
{

    uint8_t status = 0;
    uint32_t addrx = 0;
    uint32_t endaddr = 0;

    if (waddr < STM32_FLASH_BASE || waddr % 4 ||        /* 写入地址小于 STM32_FLASH_BASE, 或不是4的整数倍, 非法. */
        waddr > (STM32_FLASH_BASE + STM32_FLASH_SIZE))  /* 写入地址大于 STM32_FLASH_BASE + STM32_FLASH_SIZE, 非法. */
    {
        return;
    }

    stmflash_unlock();              /* FLASH解锁 */

    FLASH->ACR &= ~(1 << 10);       /* FLASH擦除期间,必须禁止数据缓存!!!搞了我两晚上才发现这个问题! */
    addrx = waddr;                  /* 写入的起始地址 */
    endaddr = waddr + length * 4;   /* 写入的结束地址 */

    if (addrx < 0X1FFF0000)         /* 只有主存储区,才需要执行擦除操作!! */
    {
        while (addrx < endaddr)     /* 扫清一切障碍.(对非FFFFFFFF的地方,先擦除) */
        {
            if (stmflash_read_word(addrx) != 0XFFFFFFFF)    /* 有非0XFFFFFFFF的地方,要擦除这个扇区 */
            {
                status = stmflash_erase_sector(stmflash_get_flash_sector(addrx));

                if (status)break;   /* 发生错误了 */
            }
            else
            {
                addrx += 4;
            }
        }
    }

    if (status == 0)
    {
        while (waddr < endaddr)     /* 写数据 */
        {
            if (stmflash_write_word(waddr, *pbuf))  /* 写入数据 */
            {
                break;              /* 写入异常 */
            }

            waddr += 4;
            pbuf++;
        }
    }

    FLASH->ACR |= 1 << 10;          /* FLASH擦除结束,开启数据fetch */

    stmflash_lock();                /* 上锁 */
}

void stmflash_write16(uint32_t waddr, uint16_t *pbuf, uint16_t length)
{

    uint8_t status = 0;
    uint32_t addrx = 0;
    uint32_t endaddr = 0;

    if (waddr < STM32_FLASH_BASE || waddr % 4 ||        /* 写入地址小于 STM32_FLASH_BASE, 或不是4的整数倍, 非法. */
        waddr > (STM32_FLASH_BASE + STM32_FLASH_SIZE))  /* 写入地址大于 STM32_FLASH_BASE + STM32_FLASH_SIZE, 非法. */
    {
        return;
    }

    stmflash_unlock();              /* FLASH解锁 */

    FLASH->ACR &= ~(1 << 10);       /* FLASH擦除期间,必须禁止数据缓存!!!搞了我两晚上才发现这个问题! */
    addrx = waddr;                  /* 写入的起始地址 */
    endaddr = waddr + length * 4;   /* 写入的结束地址 */

    if (addrx < 0X1FFF0000)         /* 只有主存储区,才需要执行擦除操作!! */
    {
        while (addrx < endaddr)     /* 扫清一切障碍.(对非FFFFFFFF的地方,先擦除) */
        {
            if (stmflash_read_word(addrx) != 0XFFFFFFFF)    /* 有非0XFFFFFFFF的地方,要擦除这个扇区 */
            {
                status = stmflash_erase_sector(stmflash_get_flash_sector(addrx));

                if (status)break;   /* 发生错误了 */
            }
            else
            {
                addrx += 2;
            }
        }
    }

    if (status == 0)
    {
        while (waddr < endaddr)     /* 写数据 */
        {
            if (stmflash_write_word(waddr, *pbuf))  /* 写入数据 */
            {
                break;              /* 写入异常 */
            }

            waddr += 2;
            pbuf++;
        }
    }

    FLASH->ACR |= 1 << 10;          /* FLASH擦除结束,开启数据fetch */

    stmflash_lock();                /* 上锁 */
}

/**
 * @brief       从指定地址开始读出指定长度的数据
 * @param       raddr : 起始地址
 * @param       pbuf  : 数据指针
 * @param       length: 要读取的 字(32位)数(就是要读取的32位数据的个数)
 * @retval      无
 */
void stmflash_read(uint32_t raddr, uint32_t *pbuf, uint32_t length)
{
    uint32_t i;

    for (i = 0; i < length; i++)
    {
        pbuf[i] = stmflash_read_word(raddr);    /* 读取4个字节(1个字) */
        raddr += 4;                             /* 偏移4个字节 */
    }
}

void stmflash_read16(uint32_t raddr, uint16_t *pbuf, uint16_t length)
{
    uint32_t i;

    for (i = 0; i < length; i++)
    {
        pbuf[i] = stmflash_read_word(raddr);    /* 读取4个字节(1个字) */
        raddr += 2;                             /* 偏移4个字节 */
    }
}

/******************************************************************************************/
/* 测试用代码 */

/**
 * @brief       测试写数据(写1个字)
 * @param       waddr : 起始地址
 * @param       wdata : 要写入的数据
 * @retval      无
 */
void test_write(uint32_t waddr, uint32_t wdata)
{
    stmflash_write(waddr, &wdata, 1);       /* 写入一个字 */
}


uint16_t Flash_GetSector(uint32_t Address)
{
    uint16_t sector = 0;
    if((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0))
    {
        sector = FLASH_SECTOR_0;
    }
    else if((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1))
    {
        sector = FLASH_SECTOR_1;
    }
    else if((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2))
    {
        sector = FLASH_SECTOR_2;
    }
    else if((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3))
    {
        sector = FLASH_SECTOR_3;
    }
    else if((Address < ADDR_FLASH_SECTOR_5) && (Address >= ADDR_FLASH_SECTOR_4))
    {
        sector = FLASH_SECTOR_4;
    }
    else if((Address < ADDR_FLASH_SECTOR_6) && (Address >= ADDR_FLASH_SECTOR_5))
    {
        sector = FLASH_SECTOR_5;
    }
    else if((Address < ADDR_FLASH_SECTOR_7) && (Address >= ADDR_FLASH_SECTOR_6))
    {
        sector = FLASH_SECTOR_6;
    }
    else if((Address < ADDR_FLASH_SECTOR_8) && (Address >= ADDR_FLASH_SECTOR_7))
    {
        sector = FLASH_SECTOR_7;
    }
    else if((Address < ADDR_FLASH_SECTOR_9) && (Address >= ADDR_FLASH_SECTOR_8))
    {
        sector = FLASH_SECTOR_8;
    }
    else if((Address < ADDR_FLASH_SECTOR_10) && (Address >= ADDR_FLASH_SECTOR_9))
    {
        sector = FLASH_SECTOR_9;
    }
    else if((Address < ADDR_FLASH_SECTOR_11) && (Address >= ADDR_FLASH_SECTOR_10))
    {
        sector = FLASH_SECTOR_10;
    }
    else /* (Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_5) */
    {
        sector = FLASH_SECTOR_11;
    }
    return sector;
}

uint32_t STMFLASH_ReadWord(uint32_t faddr)
{
    return *(__IO uint32_t *)faddr;
}

void Flash_Read8BitDatas(uint32_t address, uint16_t length, int8_t* data_8)
{
    uint16_t i;
    for (i = 0; i<length; i++)
    {
        data_8[i] = *(__IO int8_t*)address;
        address++;
    }
}

void Flash_Read16BitDatas(uint32_t address, uint16_t length, int16_t* data_16)
{
    uint8_t i;
    for (i = 0; i<length; i++)
    {
        data_16[i] = *(__IO int16_t*)address;
        address = address + 2;
    }
}

void Flash_Read32BitDatas(uint32_t address, uint16_t length, int32_t* data_32)
{
    uint16_t i;
    for (i = 0; i<length; i++)
    {
        data_32[i] = *(__IO int32_t*)address;
        address = address + 4;
    }
}


void Flash_EraseSector(uint32_t Address)
{
    FLASH_EraseInitTypeDef FlashEraseInit;
    uint32_t SectorError=0;

    HAL_FLASH_Unlock();

    FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS;       //擦除类型，扇区擦除
    FlashEraseInit.Sector=Flash_GetSector(Address);   		//要擦除的扇区
    FlashEraseInit.NbSectors=1;                             //一次只擦除一个扇区
    FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3;      //电压范围，VCC=2.7~3.6V之间!!
    if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK)
    {
        printf("[FLASH]erase error\n");
    }

    HAL_FLASH_Lock();
}

void Flash_Write8BitDatas(uint32_t address, uint16_t length, int8_t* data_8)
{
    HAL_StatusTypeDef FlashStatus=HAL_OK;
    uint32_t endaddr=0;

    HAL_FLASH_Unlock();             //解锁
    endaddr=address+length;	//写入的结束地址

    FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME);            //等待上次操作完成
    if(FlashStatus == HAL_OK)
    {
        while(address < endaddr)//写数据
        {
            if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE,address,*data_8)!=HAL_OK)//写入数据
            {
                printf("[FLASH]error write.");
                break;	//写入异常
            }
            address++;
            data_8++;
        }
    }
    HAL_FLASH_Lock();           //上锁
}


void Flash_Write16BitDatas(uint32_t address, uint16_t length, int16_t* data_16)
{
    HAL_StatusTypeDef FlashStatus=HAL_OK;
    uint32_t endaddr=0;

    HAL_FLASH_Unlock();             //解锁
    endaddr=address+length*2;	//写入的结束地址

    FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME);            //等待上次操作完成
    if(FlashStatus == HAL_OK)
    {
        while(address < endaddr)//写数据
        {
            if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,address,*data_16)!=HAL_OK)//写入数据
            {
                printf("[FLASH]error write.");
                break;	//写入异常
            }
            address += 2;
            data_16++;
        }
    }
    HAL_FLASH_Lock();           //上锁
}

HAL_StatusTypeDef Flash_Write32BitDatas(uint32_t address, uint16_t length, int32_t* data_32)
{
    HAL_StatusTypeDef FlashStatus=HAL_OK;
    HAL_StatusTypeDef WriteFlash = HAL_OK;
    HAL_StatusTypeDef  Return_sttus = HAL_OK;
    uint32_t endaddr=0;

    HAL_FLASH_Unlock();         //解锁
    endaddr=address+length*4;		//写入的结束地址
    FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME);            //等待上次操作完成
    if(FlashStatus == HAL_OK)
    {

        while(address < endaddr)//写数据
        {

            Return_sttus =  HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,address,*data_32);
           // printf("WriteFlash = %d\r\n",WriteFlash);

            if(Return_sttus !=HAL_OK)//写入数据
            {
                printf("[FLASH]error write.");

                return Return_sttus;	//写入异常
            }
            address += 4;
            data_32++;
        }
    }
    HAL_FLASH_Lock();           //上锁
    return Return_sttus;
}

int8_t Flash_WriteAddrCheck(uint32_t start_addr,uint32_t end_addr)
{
    int8_t *backup_data = NULL;
    int8_t need_copy = 0;
    uint32_t start_address = start_addr;
    uint32_t malloc_len = 0;
    HAL_StatusTypeDef FlashStatus=HAL_OK;
    if(start_addr%4 != 0)
    {
        printf("[stm_flash] write addr not mem align\r\n");
        return -1;
    }
    if((start_addr >= STM32_FLASH_END_ADDR) || (start_addr < STM32_FLASH_BASE_ADDR))
    {
        printf("[stm_flash] write addr is not correct addr\r\n");
        return -1;
    }
    if(start_addr < FLASH_SAVE_ADDR)
    {
        printf("[stm_flash] write addr is not user data domain\r\n");
        return -1;
    }
    if(end_addr >= STM32_FLASH_END_ADDR)
    {
        printf("[stm_flash] write len is more than\r\n");
        return -1;
    }
    while(start_addr < end_addr)
    {
        if(STMFLASH_ReadWord(start_addr) != 0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区
        {
            need_copy = 0x01;
            break;
        }
        else
        {
            start_addr += 4;
        }
    }
    start_addr = start_address;
    if(need_copy == 0x01)
    {
        HAL_FLASH_Unlock();
        malloc_len = start_addr - FLASH_SAVE_ADDR;
        if(malloc_len > 0)
        {
            backup_data = malloc(malloc_len);
            if(backup_data != NULL)
            {
                Flash_Read8BitDatas(FLASH_SAVE_ADDR,malloc_len,backup_data);
            }
            else
            {
                printf("[stm_flash] malloc mem faield\r\n");
                return -1;
            }
        }
        Flash_EraseSector(start_addr);
        FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME);//等待上次操作完成
        if(FlashStatus == HAL_OK)
        {
            Flash_Write8BitDatas(FLASH_SAVE_ADDR,malloc_len,backup_data);
        }
        else
        {
            printf("[stm_flash] write data faield\r\n");
            return -1;
        }
        HAL_FLASH_Lock();           //上锁
    }
    if(backup_data != NULL)
    {
        free(backup_data);
    }
    return 0;
}


void read_param(uint32_t add_off, int8_t* data_8, uint16_t length)
{
    Flash_Read8BitDatas(FLASH_SAVE_ADDR + add_off, length, data_8);
}

void write_param(uint32_t add_off, int8_t* data_8, uint16_t length)
{
    uint32_t start_addr = FLASH_SAVE_ADDR + add_off;
    uint32_t end_addr = FLASH_SAVE_ADDR + add_off + length;
    if(Flash_WriteAddrCheck(start_addr, end_addr) == 0)
    {
        Flash_Write8BitDatas(FLASH_SAVE_ADDR + add_off, length, data_8);
    }
}


void read_param16(uint32_t add_off, int16_t* data_16, uint16_t length)
{
    Flash_Read16BitDatas(FLASH_SAVE_ADDR + add_off, length, data_16);
}

void write_param16(uint32_t add_off, int16_t* data_16, uint16_t length)
{
    uint32_t start_addr = FLASH_SAVE_ADDR + add_off;
    uint32_t end_addr = FLASH_SAVE_ADDR + add_off + length*2;
    if(Flash_WriteAddrCheck(start_addr, end_addr) == 0)
    {
        Flash_Write16BitDatas(FLASH_SAVE_ADDR + add_off, length, data_16);
    }
}

void read_param32(uint32_t add_off, int32_t* data_32, uint16_t length)
{
    Flash_Read32BitDatas(FLASH_SAVE_ADDR + add_off, length, data_32);
}

void write_param32(uint32_t add_off, int32_t* data_32, uint16_t length)
{
    uint32_t start_addr = FLASH_SAVE_ADDR + add_off;
    uint32_t end_addr = FLASH_SAVE_ADDR + add_off + length*4;
    if(Flash_WriteAddrCheck(start_addr, end_addr) == 0)
    {
        Flash_Write32BitDatas(FLASH_SAVE_ADDR + add_off, length, data_32);
    }
}

void stm_flash_unlock(void)
{
    HAL_FLASH_Unlock();
}

void stm_flash_lock(void)
{
    HAL_FLASH_Lock();
}


















